
/** Cette classe permet une représentation plus efficace des données
 * pour mieux gérer les calculs intermédiaires.
 * @author Pocman
 *
 */
public class AntSolution implements Cloneable{
	

	private Instance instance;
	private int[] solution;
	private int[] arcs;
	private double[] distances;
	private boolean longueurACalculer;
	private double longueurSolution;
	
	/** Constructeur de AntSolution
	 */
	public AntSolution(Instance inst) {
		this.instance = inst;
		this.solution = new int[this.instance.getNbSommets()];
		this.arcs = new int[this.instance.getNbSommets()];
		this.distances = new double[this.instance.getNbSommets()];
		this.init();
	}
	/** Fonction init
	 * initialise les variables d'instance
	 */
	public void init(){
		this.longueurSolution = 0;		
		for(int i = 0; i < this.solution.length; i++){
			this.solution[i] = -1;
			this.arcs[i] = -1;
			this.distances[i] = Double.MAX_VALUE;
		}
	}
	
	/**Fonction getNoeud
	 * retourne le nombre de noeud de l'instance testee
	 */
	public int getNoeud(int i) {
		int n;
		
		i= this.mod(i,this.instance.getNbSommets());
		
		try {
			n= this.solution[i];
		} catch (Exception e) {
			n=-1;
			e.printStackTrace();
		}
		return n;
	}
	
	/** Fonction setNoeud
	 *  rajoute un noeud entre 2 arc existant
	 */
	public void setNoeud(int s, int i){
	
		i= this.mod(i,this.instance.getNbSommets());
		s= this.mod(s,this.instance.getNbSommets());
		
		
		try {
			if(this.getLongueurArc(i) != Double.MAX_VALUE){
				this.longueurSolution -= this.getLongueurArc(i);
			}
			if(i == 0){
				if(this.getLongueurArc(this.instance.getNbSommets() -1) != Double.MAX_VALUE){
					this.longueurSolution -= this.getLongueurArc(this.instance.getNbSommets() -1);
				}
			}
			else{
				if(this.getLongueurArc(i-1) != Double.MAX_VALUE){
					this.longueurSolution -= this.getLongueurArc(i -1);
				}
			}
				
			this.solution[i] = s;
			
			//arc avec noeud précédent si il existe
			if(i != 0 && this.getNoeud(i-1) != -1){
				this.arcs[this.getNoeud(i-1)] = s;
			}
			else if(i == 0 && this.getNoeud(this.instance.getNbSommets()-1) != -1){
				this.arcs[this.getNoeud(this.instance.getNbSommets() -1)] = s;
			}
			//arc avec noeud suivant si il existe
			if(i != this.instance.getNbSommets() -1 ){
				this.arcs[s] = this.getNoeud(i+1);
			}
			else if(i == this.instance.getNbSommets() -1){
				this.arcs[s] = this.getNoeud(0);
			}
			
			//mise a jour longueur arc suivant
			if(this.isArc(s, this.getNoeud(i+1))){
				this.distances[i] = this.getDistance(i, i+1);
				this.longueurSolution += this.distances[i];
			}
			//mise a jour longueur arc précédent
			if(this.isArc(this.getNoeud(i-1), s )){
				this.distances[i-1] = this.getDistance(i-1,i);
				this.longueurSolution += this.distances[i-1];
			}
			
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	//entre le noeud n°i et le noeud n°j
	public boolean isArc(int i, int j){
		
		i= this.mod(i,this.instance.getNbSommets());
		j= this.mod(j,this.instance.getNbSommets());
		
		return this.arcs[i] == j;
	}
	/** Fonction inverserArcs
	 * inverse le sens d'un arc donc l'ordre de parcours des sommets
	 */
	public void inverserArcs(int i, int j){
		//System.err.println("" + j + "/" + i);
		if (j > i) {
			for (int k = 0; k <= (int) (Math
					.floor((double) (j - (i)) / (double) (2))); k++) {
				int tmp = this.getNoeud(i + k);
				this.setNoeud(this.getNoeud(j - k), i + k);
				this.setNoeud(tmp, j - k);
			}
		} else {
			for (int k = 0; k <= (int) (Math
					.floor((double) (i - 1 - (j+1)) / (double) (2))); k++) {
				int tmp = this.getNoeud(j + 1 + k);
				this.setNoeud(this.getNoeud(i - 1 - k), j + 1 + k);
				this.setNoeud(tmp, i - 1 - k);
			}
		}
	}
	
	/** Fonction getLongueurArc
	 * Retourne la longueur du ieme arc de la solution
	 */
	public double getLongueurArc(int i){
		
		i= this.mod(i,this.instance.getNbSommets());
		
		return this.distances[i];
	}
	
	/**Fonction getLongueurSolution
	 * retourne la longueur de la solution en memoire
	 */
	public double getLongueurSolution(){
		return this.longueurSolution;
	}

	/**Fonction setSolution
	 * Met la solution à la valeur solution s
	 */
	public void setSolution(Solution s){
		for(int i=1; i<= this.instance.getNbSommets(); i++){
			try {
				this.setNoeud(s.getSolution(i)-1, i-1);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	/**Fonction getSolution
	 * retourne la solution 
	 */
	public Solution getSolution(){
		Solution s = new Solution(this.instance);
		for(int i=1; i<= this.instance.getNbSommets(); i++){
			try {
				s.setVertexPosition(this.solution[i-1] + 1, i);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return s;
	}

	/**Fonction getDistance
	 * distance entre le ieme noeud et le jeme noeud de la solution
	 */
	public double getDistance(int i, int j) throws Exception{

		int noeud1 = this.getNoeud(i) + 1;
		int noeud2 = this.getNoeud(j) + 1;
		return this.instance.getDistances(noeud1, noeud2);
	}
	
	/**Fonction isBest
	 *Retourne un booleen vrai si la longueur de la solution actuelle est inferieure à la longueur de la solution de comparaison s
	 */
	public boolean isBest(AntSolution s){
		if(this.getLongueurSolution() == s.getLongueurSolution()){
			boolean isDifferent = false;
			for(int i = 1; !isDifferent && i < this.instance.getNbSommets() -1; i++){
				isDifferent = !s.isArc(this.getNoeud(i), this.getNoeud(i+1)) && !s.isArc(this.getNoeud(i),this.getNoeud(i-1)) ;
			}
			return isDifferent;

		}
		else 		if(this.getLongueurSolution() < s.getLongueurSolution()){
			return true;		
		}
		else
			return false;
	}
	
	/**Fonction mod
	 *retourne le resultat de la division euclidienne de x par n
	 */
	public int mod(int x, int n){
		int r = x % n;
		if (r < 0)
		{
		    r += n;
		}
		return r;
	}
	
	/**Fonction insererArcs
	 * Inserer les noeuds d'indices j à k derriere i
	 * retourne la AntSolution modifiee
	 */
	public AntSolution insererArcs(int i,int j, int k){
		
		AntSolution newSolution=new AntSolution(this.instance);
		if (i < j) {
			for (int m =0; m <= i; m++) {
				newSolution.setNoeud(this.getNoeud(m), m);
			}
			for (int m = k+1; m<this.distances.length; m++) {
				newSolution.setNoeud(this.getNoeud(m), m);
			}
			for (int m=0; m<=k-j; m++) {
				newSolution.setNoeud(this.getNoeud(m+j), i+1+m);
			}
			for (int m = 0; m <= j-i-2; m++) {
				newSolution.setNoeud(this.getNoeud(m+i+1), i+m+k-j+2);
			}
		} else {
			for (int m = 0; m<j; m++) {
				newSolution.setNoeud(this.getNoeud(m), m);
			}
			for (int m =i+1; m<=this.distances.length; m++) {
				newSolution.setNoeud(this.getNoeud(m), m);
			}
			for (int m = 0; m <=i-k; m++) {
				newSolution.setNoeud(this.getNoeud(k+1+m), j+m);
			}
			for (int m = 0; m<=k-j; m++) {
				newSolution.setNoeud(this.getNoeud(j+m), j+i-k+m);
			}
			
		}

		return newSolution;
}

	/**Fonction getPositionNoeud
	 * retourne la position du noeud j dans l'instance
	 */
	public int getPositionNoeud(int j) {
		for(int i = 0; i < this.solution.length; i++){
			if(this.solution[i] == j){
				return i;
			}
		}
		return -1;
	}
	
}